using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace OpenComposite.Client.Smart.Controls
{
	[Serializable,
	DesignTimeVisible(false),
	ToolboxItem(false)]
	public class ColumnHeaderEx : ColumnHeader
	{
		#region Fields

		private string m_dataColumnName;
		private DisplayType m_displayType;

		#endregion Fields

		#region Constructors

		public ColumnHeaderEx()
			: base()
		{
			m_dataColumnName = "";
			m_displayType = DisplayType.Default;
		}

		public ColumnHeaderEx(string imageKey)
			: base(imageKey)
		{
			m_dataColumnName = "";
			m_displayType = DisplayType.Default;
		}

		public ColumnHeaderEx(int imageIndex)
			: base(imageIndex)
		{
			m_dataColumnName = "";
			m_displayType = DisplayType.Default;
		}

		#endregion Constructors

		#region Public Properties

		[DefaultValue(DisplayType.Default)]
		public DisplayType ColumnDisplayType
		{
			get { return m_displayType; }
			set { m_displayType = value; }
		}

		public string DataColumnName
		{
			get { return m_dataColumnName; }
			set { m_dataColumnName = value; }
		}

		#endregion Public Properties

		#region Public Methods

		public new object Clone()
		{
			ColumnHeaderEx col = (ColumnHeaderEx)base.Clone();
			col.DataColumnName = this.DataColumnName;
			col.ColumnDisplayType = this.ColumnDisplayType;
			return col;
		}

		#endregion Public Methods
	}

	/// <summary>
	/// An ListViewEx.
	/// </summary>
	[ToolboxBitmap(typeof(ListView)),
	ToolboxItem(true)]
	public class ListViewEx : System.Windows.Forms.ListView
	{
		#region Constants

		private const int WM_NCPAINT = 0x85;
		private const int WM_PAINT = 0xf;

		#endregion Constants

		#region Fields

		private ContextMenu m_RightClickMenu = null;
		private ContextMenuStrip m_RightClickMenuStrip = null;
		private string[] m_arrStatusCDTranslation;
		private bool m_bShowDataSetColumns = false;
		private bool m_bSortAsc;
		private System.Drawing.Color m_colorAlterBack = Color.Gainsboro;
		private System.Drawing.Color m_colorBack = Color.White;
		private ListViewEx.ColumnHeaderCollectionEx m_columns;
		private DataTable m_data;
		private string m_sIDColumnNm = "";
		private string m_sSortColNm;

		#endregion Fields

		#region Constructors

		/// <summary>
		/// Initialize ListViewEx.
		/// </summary>
		public ListViewEx()
			: base()
		{
			m_sSortColNm	= "";
			m_bSortAsc		= false;

			base.View = View.Details;
			base.FullRowSelect = true;
			base.HideSelection = false;

			this.ColumnClick += new ColumnClickEventHandler(this_ColumnClick);
			this.MouseDown += new MouseEventHandler(this_MouseDown);

			m_columns = new ColumnHeaderCollectionEx(this);

			// stat_cd
			//  Case 0: "Not Started"
			//  Case 1: "In Progress"
			//	Case 2: "Completed"
			//	Case 3: "Stopped"
			//	Case 4: "Deferred"
			//	Case 5: "Waiting"
			m_arrStatusCDTranslation = new string[] {"Not Started", "In Progress", "Completed", "Stopped", "Deferred", "Waiting"};
		}

		#endregion Constructors

		#region Events

		/// <summary>
		/// Occurs before an item is added to the listview and allows to change the appearance of the item.
		/// </summary>
		[Category("Appearance")]
		public event ItemSettingsEventHandler ItemSettings;

		#endregion Events

		#region Public Properties

		[Category("Appearance"),
		DefaultValue(typeof(System.Drawing.Color),"Gainsboro")]
		public System.Drawing.Color AlternatingLineBackColor
		{
			get { return m_colorAlterBack; }
			set { m_colorAlterBack = value; }
		}

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		Category("Behavior")]
		public new ListViewEx.ColumnHeaderCollectionEx Columns
		{
			get { return m_columns; }
		}

		[Browsable(false),
		Category("Data")]
		public DataTable Data
		{
			get { return m_data; }
			set { m_data = value; }
		}

		[DefaultValue(true)]
		public new bool FullRowSelect
		{
			get { return base.FullRowSelect; }
			set { base.FullRowSelect = value; }
		}

		[DefaultValue(false)]
		public new bool HideSelection
		{
			get { return base.HideSelection; }
			set { base.HideSelection = value; }
		}

		[DefaultValue(""),
		Category("Data")]
		public string IDColumnName
		{
			get { return m_sIDColumnNm; }
			set { m_sIDColumnNm = value; }
		}

		[Category("Appearance"),
		DefaultValue(typeof(System.Drawing.Color), "White")]
		public System.Drawing.Color LineBackColor
		{
			get { return m_colorBack; }
			set { m_colorBack = value; }
		}

		[DefaultValue(null),
		Category("Behavior")]
		public ContextMenu RightClickMenu
		{
			get { return m_RightClickMenu; }
			set {
				if ( value != null )
					m_RightClickMenuStrip = null;
				m_RightClickMenu = value;
			}
		}

		[DefaultValue(null),
		Category("Behavior")]
		public ContextMenuStrip RightClickMenuStrip
		{
			get { return m_RightClickMenuStrip; }
			set {
				if ( value != null )
					m_RightClickMenu = null;
				m_RightClickMenuStrip = value;
			}
		}

		[Category("Data"),
		DefaultValue(false)]
		public bool ShowAllDataSetColumns
		{
			get { return m_bShowDataSetColumns; }
			set { m_bShowDataSetColumns = value; }
		}

		[DefaultValue(typeof(View), "Details")]
		public new View View
		{
			get { return base.View; }
			set { base.View = value; }
		}

		#endregion Public Properties

		#region Public Methods

		/// <summary>
		/// Create columnheaders and fill the listview.
		/// </summary>
		public void Fill()
		{
			createColumns();
			if ( m_data != null ) {
				this.BeginInvoke(new fillListDelegate(fillList), new object[] { m_data.Select() });
				//fillList(m_data.Select());
			}
		}

		/// <summary>
		/// Returns the DataRows of the checked items.
		/// </summary>
		/// <returns>Array of DataRows.</returns>
		public DataRow[] GetCheckedRows()
		{
			DataRow[] rows = new DataRow[this.CheckedItems.Count];
			for ( int i=0; i<this.CheckedItems.Count; i++ )
				rows[i] = (DataRow)this.CheckedItems[i].Tag;
			return rows;
		}

		/// <summary>
		/// Returns the DataRows of the selected items.
		/// </summary>
		/// <returns>Array of DataRows.</returns>
		public DataRow[] GetSelectedRows()
		{
			DataRow[] rows = new DataRow[this.SelectedItems.Count];
			for ( int i=0; i<this.SelectedItems.Count; i++ )
				rows[i] = (DataRow)this.SelectedItems[i].Tag;
			return rows;
		}

		/// <summary>
		/// Fill the listview with the filtered rows.
		/// </summary>
		/// <param name="filterExpression">A filter expression.</param>
		public void Select(string filterExpression)
		{
			if ( m_data == null ) {
				Debug.WriteLine("ListViewEx.m_data == NULL");
				return;
			}
			this.BeginInvoke(new fillListDelegate(fillList), new object[] { m_data.Select(filterExpression) });
			//fillList(m_data.Select(filterExpression));
		}

		/// <summary>
		/// Fill the listview with the filtered rows.
		/// </summary>
		/// <param name="filterExpression">A filter expression.</param>
		/// <param name="sort">A sort expression</param>
		public void Select(string filterExpression, string sort)
		{
			if ( m_data == null ) {
				Debug.WriteLine("ListViewEx.m_data == NULL");
				return;
			}
			this.BeginInvoke(new fillListDelegate(fillList), new object[] { m_data.Select(filterExpression, sort) });
			//fillList(m_data.Select(filterExpression, sort));
		}

		/// <summary>
		/// Sort the listview by a column.
		/// </summary>
		/// <param name="iCol">Index of the sort column.</param>
		public void Sort(int iCol)
		{
			string sCol = m_columns[iCol].DataColumnName;
			if ( sCol == m_sSortColNm ) {
				m_bSortAsc		= !m_bSortAsc;
			} else {
				m_sSortColNm	= sCol;
				m_bSortAsc		= true;
			}
			this.Sort(sCol, m_bSortAsc);
		}

		/// <summary>
		/// Sort the listview by a column.
		/// </summary>
		/// <param name="iCol">Index of the sort column.</param>
		/// <param name="bAsc">Sort direction.</param>
		public void Sort(int iCol, bool bAsc)
		{
			if(iCol < 0 || iCol >= this.Columns.Count)
				throw new IndexOutOfRangeException();

			this.Sort(m_columns[iCol].DataColumnName, bAsc);
		}

		/// <summary>
		/// Sort the listview by a column.
		/// </summary>
		/// <param name="sColName">Name of the sort column.</param>
		/// <param name="bAsc">Sort direction.</param>
		public void Sort(string sColName, bool bAsc)
		{
			m_sSortColNm	= sColName;
			m_bSortAsc		= bAsc;

			ListViewSort comp = 
				(ListViewSort)Activator.CreateInstance(
					typeof(ListViewSort),
					new Object[] { m_sSortColNm, m_bSortAsc }
				);
			this.ListViewItemSorter = comp;
			this.setBackColors();
		}

		#endregion Public Methods

		#region Protected Methods

		protected void OnItemSettings(ListViewItem lvItem, DataRow row)
		{
			if ( this.ItemSettings != null )
				this.ItemSettings(this, new ItemSettingsEventArgs(lvItem, row));
		}

		/// <summary>
		/// Overridden. Windows message processing.
		/// </summary>
		/// <param name="message">The Windows message to process.</param>
		protected override void WndProc( ref Message message )
		{
			// if the control is in details view mode and columns
			// have been added, then intercept the WM_NCPAINT and WM_PAINT message
			// and reset the last column width to fill the list view
			switch ( message.Msg ) {
				case WM_PAINT	:
				case WM_NCPAINT	:
					if ( this.View == View.Details && this.Columns != null && this.Columns.Count > 0 )
						this.Columns[this.Columns.Count - 1].Width = -2 ;
					break ;
			}

			// pass messages on to the base control for processing
			base.WndProc( ref message ) ;
		}

		#endregion Protected Methods

		#region Private Methods

		private void createColumns()
		{
			base.BeginUpdate();

			if ( m_bShowDataSetColumns )
				this.Columns.Clear();

			string	nm;

			if ( this.Columns.Count == 0 && m_data != null && m_data.Columns != null ) {
				for ( int i=0; i<m_data.Columns.Count; i++ ) {
					nm = m_data.Columns[i].ColumnName;
					this.Columns.Add(nm, -2, HorizontalAlignment.Left, DisplayType.Default, nm);
				}
			}

			base.EndUpdate();
		}

		private void fillList(DataRow[] rows)
		{
			int			i = 0;
			bool		bDark = false;
			ArrayList	alIDs = new ArrayList();

			if ( rows == null ) {
				Debug.WriteLine("ListViewEx.m_data == NULL");
				return;
			}
			base.BeginUpdate();
			base.Items.Clear();

			foreach ( DataRow dr in rows ) {
				ListViewItem	lvi;
				string[]		items		= new string[this.Columns.Count];
				bool			bChecked	= false;
				string			colnm		= "";
				string			sID			= "";
				
				if ( m_sIDColumnNm != null && !m_sIDColumnNm.Equals("") ) {
					sID = dr[m_sIDColumnNm].ToString();
					if ( alIDs.IndexOf(sID) > -1 )
						continue;
					else
						alIDs.Add(sID);
				}
				i = 0;
				foreach ( ColumnHeaderEx col in this.Columns ) {
					colnm = col.DataColumnName;
					if ( !dr.IsNull(colnm) ) {
						if ( dr[colnm].GetType() == typeof(DateTime) ) {
							items[i] = ((DateTime)dr[colnm]).ToString("g");
						} else if ( dr[colnm].GetType() == typeof(bool) ) {
							if ( i == 0 ) {
								items[i] = "";
								bChecked = (bool)dr[colnm];
							} else {
								items[i] = ((bool)dr[colnm]).ToString();
							}
						} else if ( col.ColumnDisplayType == DisplayType.Currency ) {
							try {
								items[i] = Convert.ToDouble(dr[colnm]).ToString("#,##0.00");
							} catch {
								items[i] = dr[colnm].ToString();
							}
						} else if ( col.ColumnDisplayType == DisplayType.Percent ) {
							try {
								items[i] = Convert.ToDouble(dr[colnm]).ToString("0.00") + " %";
							} catch {
								items[i] = dr[colnm].ToString();
							}
						} else if ( col.ColumnDisplayType == DisplayType.StatusCD ) {
							try {
								items[i] = m_arrStatusCDTranslation[Convert.ToInt32(dr[colnm])];
							} catch {
								items[i] = dr[colnm].ToString();
							}
						} else {
							items[i] = dr[colnm].ToString();
							items[i].Replace("\n", " ");
							items[i].Replace("\r\n", " ");
							items[i].Replace("\n\r", " ");
						}
					} else
						items[i] = "";

					i++;
				}
				lvi				= new ListViewItem(items);
				lvi.BackColor	= (bDark ? m_colorAlterBack : m_colorBack);
				bDark			= !bDark;
				lvi.Tag			= dr;
				lvi.Checked		= bChecked;

				OnItemSettings(lvi, dr);
				
				base.Items.Add(lvi);
			}
			this.setBackColors();
			foreach ( ColumnHeaderEx ch in this.Columns ) {
				ch.Width = -2;
				if ( ch.Index == 0 )
					ch.Width += 20;
			}
			base.EndUpdate();
		}

		private void setBackColors()
		{
			this.BeginUpdate();
			bool bDark = false;
			foreach ( ListViewItem lvi in this.Items ) {
				lvi.BackColor	= (bDark ? m_colorAlterBack : m_colorBack);
				bDark			= !bDark;
			}
			this.EndUpdate();
		}

		private void this_ColumnClick(object sender, ColumnClickEventArgs e)
		{
			Sort(e.Column);
		}

		private void this_MouseDown(object sender, MouseEventArgs e)
		{
			if ( e.Button == MouseButtons.Right ) {
				ListViewItem lvi = this.GetItemAt(e.X, e.Y);
				if ( lvi != null ) {
					if ( !lvi.Selected ) {
						this.SelectedItems.Clear();
						lvi.Selected = true;
					}
					if ( m_RightClickMenu != null )
						m_RightClickMenu.Show(this, new Point(e.X, e.Y));
					else if ( m_RightClickMenuStrip != null )
						m_RightClickMenuStrip.Show(this, new Point(e.X, e.Y));
				}
			}
		}

		#endregion Private Methods

		#region Other

		[Serializable]
		public class ColumnHeaderCollectionEx : System.Windows.Forms.ListView.ColumnHeaderCollection
		{
			#region Constructors

			public ColumnHeaderCollectionEx(ListView owner)
				: base(owner)
			{
			}

			#endregion Constructors

			#region Public Properties

			public bool IsFixedSize
			{
				get {
					return false;
				}
			}

			[Browsable(false)]
			public new bool IsReadOnly
			{
				get {
					return false;
				}
			}

			[Browsable(false)]
			public bool IsSynchronized
			{
				get {
					return true;
				}
			}

			[Browsable(false)]
			public object SyncRoot
			{
				get {
					return this;
				}
			}

			public new ColumnHeaderEx this[int index]
			{
				get  {
					return( (ColumnHeaderEx) base[index] );
				}
			}

			public new ColumnHeaderEx this[string key]
			{
				get  {
					return ((ColumnHeaderEx)base[key]);
				}
			}

			#endregion Public Properties

			#region Public Methods

			public int Add(ColumnHeaderEx value)
			{
				return (base.Add(value));
			}

			public new ColumnHeaderEx Add(string text)
			{
				ColumnHeaderEx acol = new ColumnHeaderEx();
				acol.Text = text;
				base.Add(acol);
				return acol;
			}

			public new ColumnHeaderEx Add(string text, int width)
			{
				ColumnHeaderEx acol = new ColumnHeaderEx();
				acol.Text = text;
				acol.Width = width;
				base.Add(acol);
				return acol;
			}

			public new ColumnHeaderEx Add(string text, int width, HorizontalAlignment textAlign)
			{
				ColumnHeaderEx acol = new ColumnHeaderEx();
				acol.Text = text;
				acol.Width = width;
				acol.TextAlign = textAlign;
				acol.ColumnDisplayType = DisplayType.Default;
				acol.DataColumnName = "";
				base.Add(acol);
				return acol;
			}

			public ColumnHeaderEx Add( string text, int width, HorizontalAlignment textAlign, DisplayType displayType, string sDataColumnName )
			{
				ColumnHeaderEx acol = new ColumnHeaderEx();
				acol.Text = text;
				acol.Width = width;
				acol.TextAlign = textAlign;
				acol.ColumnDisplayType = displayType;
				acol.DataColumnName = sDataColumnName;
				base.Add( acol );
				return( acol );
			}

			public void AddRange(ColumnHeaderEx[] values)
			{
				base.AddRange(values);
			}

			public bool Contains( ColumnHeaderEx value )
			{
				return( base.Contains( value ) );
			}

			public int IndexOf( ColumnHeaderEx value )
			{
				return( base.IndexOf( value ) );
			}

			public void Insert( int index, ColumnHeaderEx value )
			{
				base.Insert( index, value );
			}

			public void Remove( ColumnHeaderEx value )
			{
				base.Remove( value );
			}

			#endregion Public Methods

			#region Other

			//			}
			//				base.CopyTo(array, index);
			//			public void CopyTo(Array array, int index) {

			#endregion Other
		}

		[ToolboxItem(false)]
		public class ListViewSort : IComparer
		{
			#region Fields

			private bool m_ascending;
			private string m_column;

			#endregion Fields

			#region Constructors

			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="sortColumn">Column to be sorted</param>
			/// <param name="ascending">true, if ascending order, false otherwise</param>
			public ListViewSort(string sortColumnName, Boolean ascending)
			{
				m_column	= sortColumnName;
				m_ascending	= ascending;
			}

			#endregion Constructors

			#region Public Methods

			/// <summary>
			/// Implementation of IComparer.Compare
			/// </summary>
			/// <param name="lhs">First Object to compare</param>
			/// <param name="rhs">Second Object to compare</param>
			/// <returns>Less that zero if lhs is less than rhs. Greater than zero if lhs greater that rhs. Zero if they are equal</returns>
			public int Compare(Object x, Object y)
			{
				ListViewItem lviX = x as ListViewItem;
				ListViewItem lviY = y as ListViewItem;

				if(lviX == null || lviY == null)    // We only know how to sort ListViewItems, so return equal
					return 0;

				int		result	= 0;
				DataRow drX		= lviX.Tag as DataRow;
				DataRow drY		= lviY.Tag as DataRow;

				if ( drX == null || drY == null )
					return 0;

				if ( drX[m_column] == DBNull.Value || drY[m_column] == DBNull.Value ) {
					if ( drX[m_column] == drY[m_column] )
						result = 0;
					else if ( drX[m_column] == DBNull.Value )
						result = -1;
					else if ( drY[m_column] == DBNull.Value )
						result = 1;
				} else if ( drX[m_column].GetType() == typeof(string) ) {
					result = string.Compare((string)drX[m_column], (string)drY[m_column], true);
				} else if ( drX[m_column].GetType() == typeof(int) ) {
					result = (int)drX[m_column] - (int)drY[m_column];
				} else if ( drX[m_column].GetType() == typeof(byte) ) {
					result = (byte)drX[m_column] - (byte)drY[m_column];
				} else if ( drX[m_column].GetType() == typeof(float) ) {
					float r = (float)drX[m_column] - (float)drY[m_column];
					if(r > 0) result = 1;
					else if(r < 0)	result = -1;
					else result = 0;
				} else if ( drX[m_column].GetType() == typeof(double) ) {
					double r = (double)drX[m_column] - (double)drY[m_column];
					if(r > 0) result = 1;
					else if(r < 0)	result = -1;
					else result = 0;
				} else if ( drX[m_column].GetType() == typeof(DateTime) ) {
					result = DateTime.Compare((DateTime)drX[m_column], (DateTime)drY[m_column]);
				} else if ( drX[m_column].GetType() == typeof(bool) ) {
					if ( (bool)drX[m_column] == (bool)drY[m_column] )
						result = 0;
					else if ((bool)drX[m_column])
						result = 1;
					else if ((bool)drY[m_column])
						result = -1;
				} else {
					Debug.WriteLine("Unhandled Type: " + drX[m_column].GetType().ToString());
				}

				if(result > 0)
					result = 1;
				else if(result < 0)
					result = -1;
				else
					result = 0;

				if(!m_ascending)
					result = -result;

				return result;
			}

			#endregion Public Methods
		}

		private delegate void fillListDelegate(DataRow[] rows);

		#endregion Other
	}

	#region Enumerations

	public enum DisplayType
	{
		Default = 0,
		Currency,
		Percent,
		StatusCD
	}

	#endregion Enumerations

	/// <summary>
	/// ItemSettings Event Argument for the ItemSettingsEventHandler
	/// </summary>
	public class ItemSettingsEventArgs : System.EventArgs
	{
		#region Fields

		private readonly ListViewItem m_lvi;
		private readonly DataRow m_row;

		#endregion Fields

		#region Constructors

		/// <summary>
		/// Initialize ItemSettingsEventArgs.
		/// </summary>
		/// <param name="lvItemArg">ListViewItem.</param>
		/// <param name="rowArg">DataRow.</param>
		public ItemSettingsEventArgs(ListViewItem lvItemArg, DataRow rowArg)
		{
			m_lvi	= lvItemArg;
			m_row	= rowArg;
		}

		#endregion Constructors

		#region Public Properties

		/// <summary>
		/// Gets the DataRow of this item.
		/// </summary>
		public DataRow DataRowArg
		{
			get { return m_row; }
		}

		/// <summary>
		/// Gets the ListViewItem.
		/// </summary>
		public ListViewItem ListViewItemArg
		{
			get { return m_lvi; }
		}

		#endregion Public Properties
	}

	#region Delegates

	/// <summary>
	/// EventHandler with ItemSettingsEventArgs
	/// </summary>
	public delegate void ItemSettingsEventHandler(object sender, ItemSettingsEventArgs e);

	#endregion Delegates
}